#include<iostream>
#include<fstream>
#include<string>
#include<vector>
#include<boost/filesystem.hpp>
#include"util.hpp"

//是一个目录，下面存放的是所有HTML的网页
const std::string src_path="data/input";
const std::string output="data/raw_html/raw.txt";


typedef struct DocInfo
{
  std::string title;
  std::string content;
  std::string url;
}DocInfo_t;

bool EnumFiles(const std::string& src_path,std::vector<std::string>* files_list);
bool ParseHtml(const std::vector<std::string>& files_list,std::vector<DocInfo_t>* results);
bool SaveHtml(const std::vector<DocInfo_t>& results,const std::string output);

int main()
{
  std::vector<std::string> files_list;
  //第一步，递归式的把每个HTML文件带路径名存放到files_list中，方便后期对文件进行一个一个的文件读取
  if(!EnumFiles(src_path,&files_list))
  {
    std::cerr<<"enum files error"<<std::endl;
    return 1;
  }

  //第二步，按照files_list读取每个文件的内容，并进行解析
  std::vector<DocInfo> results;
  if(!ParseHtml(files_list,&results))
  {
    std::cerr<<"parse files error"<<std::endl;
    return 2;
  }

  //第三步，把解析完毕的各个文件的内容，写入到output，按照\3作为每个文件的分隔符
  if(!SaveHtml(results,output))
  {
    std::cerr<<"save html error"<<std::endl;
    return 3;
  }
  return 0;
}


bool EnumFiles(const std::string& src_path,std::vector<std::string>* files_list)
{
  namespace fs=boost::filesystem;
  fs::path root_path(src_path);
  if(!fs::exists(root_path))
  {
    std::cerr<<src_path<<"not exists"<<std::endl;
    return false;
  }
  //定义一个空的迭代器，用来判断递归结束
  fs::recursive_directory_iterator end;
  for(fs::recursive_directory_iterator iter(root_path);iter!=end;iter++)
  {
    if(!fs::is_regular_file(*iter))
      continue;
    if(iter->path().extension()!=".html")
      continue;
    //当前路径一定是一个合法的，以html结束的普通网页文件
    // std::cout<<iter->path().string()<<std::endl;
    files_list->push_back(iter->path().string());
  }
  return true;
}

static bool ParseTitle(const std::string& file,std::string* title)
{
  size_t begin=file.find("<title>")+std::string("<title>").size();
  if(begin==std::string::npos)
      return false;
  size_t end=file.find("</title>");
  if(end==std::string::npos)
      return false;

  if(begin>end)
      return false;

  *title=file.substr(begin,end-begin);
  return true;
}

static bool ParseContent(const std::string& file,std::string* content)
{
  //去标签，基于一个简易的状态机
  enum status
  {
    LABLE,
    CONTENT
  };

  enum status s=LABLE;
  for(char ch:file)
  {
    switch(s)
    {
      case LABLE:
        if(ch=='>') s=CONTENT;
        break;
      case CONTENT:
        if(ch=='<') s=LABLE;
        else 
        {
          if(ch=='\n') ch=' ';//我们不想保留原始文件中的\n,因为我们想用\n作文解析之后的文件分隔符
          content->push_back(ch);
        }
        break;
      default:
        break;
    }
  }
  return true;
}

static bool ParseUrl(const std::string& file_path,std::string* url)
{
  std::string url_head="https://www.boost.org/doc/libs/1_84_0/doc/html";
	std::string url_tail=file_path.substr(src_path.size());
	
	*url=url_head+url_tail;
  return true;
}

void ShowDoc(const DocInfo_t& doc)
{
  std::cout<<"title: "<<doc.title<<std::endl;
  std::cout<<"content: "<<doc.content<<std::endl;
  std::cout<<"url: "<<doc.url<<std::endl;
}

bool ParseHtml(const std::vector<std::string>& files_list,std::vector<DocInfo_t>* results)
{
  for(const std::string& file:files_list)
  {
    //1.解读文件，Read()
    std::string  result;
    if(!ns_util::FileUtil::ReadFile(file,&result))
        continue;
    //2.解析指定的文件，提取title
    DocInfo_t doc;
    if(!ParseTitle(result,&doc.title))
        continue;
    // std::cout<<doc.title<<std::endl;
    //3.解析指定的文件，提取content
    if(!ParseContent(result,&doc.content))
        continue;
    //std::cout<<doc.content<<std::endl;
    //4.解析指定的文件，构建URL
    if(!ParseUrl(file,&doc.url))
        continue;
    //std::cout<<doc.url<<std::endl;
    //done,一定完成了解析任务，当前文档的内容都保存在了doc内
    results->push_back(std::move(doc));//本质会发生拷贝，效率比较低

    //for dubug
    //ShowDoc(doc);
  }
  return true;
}

bool SaveHtml(const std::vector<DocInfo_t>& results,const std::string output)
{
#define SEP '\3'
  std::ofstream out(output,std::ios::out|std::ios::binary);
  if(!out.is_open())
  {
      std::cerr<<"open "<<output<<" failed"<<std::endl;
      return false;
  }

  //对results内的内容进行写入
  //写入格式:xxxxx\3xxxx\3xxxxxx\n
  for(auto& doc:results)
  {
      std::string out_string;
      out_string+=doc.title;
      out_string+=SEP;
      out_string+=doc.content;
      out_string+=SEP;
      out_string+=doc.url;
      out_string+='\n';
      out.write(out_string.c_str(),out_string.size());
  }
  out.close();
  return true;
}
